from PyQt6 import QtCore, QtGui
from PyQt6.QtCore import QRect
from PyQt6.QtGui import QPixmap, QPainter, QFontDatabase, QColor, QPalette, QPen, QFont, QFontInfo, QFontMetrics

from pyte import HistoryScreen
from pyte.screens import Char, wcwidth

import logging
from functools import partial
from .Palette import Palette, Role, Background, Cursor


class QtScreenConfig(object):
    def __init__(self):
        self.logger = logging.getLogger()

        self.padding_top = 5
        self.padding_left = 5
        self.padding_bottom = 5
        self.padding_right = 5

        self.min_terminal_height: int = 24
        self.min_terminal_width: int = 80

        self.max_terminal_height: int = 24
        self.max_terminal_width: int = 80

        self.palette: Palette = Palette()

        self._line_height_factor = 1.2
        self._font_size: int = 12
        self._font: QFont = None

        self._metrics: QFontMetrics = None

        self._char_width: int = None
        self._char_height: int = None
        self._line_height: int = None

        self._after_set_font()

    def _after_set_font(self):
        families = QFontDatabase.families()
        font = self._font

        if font:
            info = QFontInfo(font)
            if info.styleHint() != QFont.Monospace:
                self.logger.warning("font: Please use monospaced font! "
                                    f"Unsupported font {info.family()}.")
                # font = qfd.systemFont(QFontDatabase.FixedFont)
                font = QFontDatabase.systemFont(QFontDatabase.SystemFont.FixedFont)
        elif "Menlo" in families:
            font = QFont("Menlo")
            info = QFontInfo(font)
        elif "Consolas" in families:
            font = QFont("Consolas")
            info = QFontInfo(font)
        else:
            font = QFontDatabase.systemFont(QFontDatabase.SystemFont.FixedFont)
            info = QFontInfo(font)

        font.setPointSize(self._font_size)
        self._font = font
        self._metrics = QFontMetrics(font)
        self._char_width = self._metrics.horizontalAdvance("A")
        self._char_height = self._metrics.height()
        self._line_height = int(self._char_height * self._line_height_factor)

        self.logger.info(f"font: Font {info.family()} selected, character "
                         f"size {self.char_width}x{self.char_height}.")

    @property
    def char_width(self) -> int:
        return self._char_width

    @property
    def char_height(self) -> int:
        return self._char_height

    @property
    def line_height(self) -> int:
        return self._line_height

    @property
    def metrics(self) -> int:
        return self._metrics

    @property
    def font(self) -> QFont:
        return self._font

    @property
    def font_size(self) -> int:
        return self._font_size

    @font.setter
    def set_font(self, font: QFont) -> None:
        self._font = font
        self._after_set_font()

    @font_size.setter
    def set_font_size(self, font_size: int) -> None:
        self._font_size = font_size
        self._after_set_font()

    def calc_char_width(self, ch: Char) -> int:
        ch_data = ch.data

        if wcwidth(ch_data[0]) == 2:
            return self.char_width * 2
        else:
            return self.char_width


class QtScreen(HistoryScreen, object):
    def __init__(self, columns, lines, history=100, config: QtScreenConfig = None, ratio=.5):
        super(QtScreen, self).__init__(columns, lines, history=history, ratio=ratio)
        self._config: QtScreenConfig = config if config is not None else QtScreenConfig()
        self._painter_lock = QtCore.QRecursiveMutex()
        self._overload_lock = QtCore.QRecursiveMutex()
        self._canvas: QPixmap = self._create_canvas()

    def _create_canvas(self) -> QPixmap:
        canvas = QPixmap(self.graphic_width, self.graphic_height)
        canvas.fill(self.background)
        return canvas

    def _x_pos(self, column: int):
        return self.config.padding_left + column * self.config.char_width

    def _y_pos(self, line: int):
        return self.config.padding_top + line * self.config.line_height

    def _char_width(self, line: int, column: int) -> int:
        ch: Char = self.buffer[line][column]
        if len(ch.data) > 0:
            char_width = self.config.calc_char_width(ch)
            return char_width
        return 0

    def _draw_char(self, line: int, column: int, paint: QPainter, clear_rect=False):
        ch: Char = self.buffer[line][column]
        if len(ch.data) > 0:

            char_width = self.config.calc_char_width(ch)

            x = self._x_pos(column)
            y = self._y_pos(line)

            fg = self.palette.get_color(ch.fg, Role.Foreground)

            if clear_rect:
                bg = self.palette.get_color(ch.bg, Role.Background)
                paint.fillRect(x, y, char_width, self.config.line_height, bg)

            paint.setFont(self.config.font)
            paint.setPen(fg)
            paint.drawText(x, y + self.config.char_height, ch.data)

    def _draw_line(self, line: int, paint: QPainter):
        paint.fillRect(0, self._y_pos(line), self.graphic_width, self.config.line_height, self.background)
        for column in range(self.columns):
            self._draw_char(line, column, paint, clear_rect=True)

    def _draw_dirty(self, lines: list=None):
        self._painter_lock.lock()
        dirty = lines if lines is not None else self.dirty
        if len(dirty) > 0:

            p = QPainter(self._canvas)
            for dirty_index in dirty:
                if dirty_index < self.lines:
                    pass
                    self._draw_line(dirty_index, p)
            p.end()

        self._painter_lock.unlock()

    @property
    def graphic_width(self):
        return self.columns * self.config.char_width + \
            self.config.padding_left + self.config.padding_right

    @property
    def graphic_height(self):
        return self.lines * self.config.line_height + \
            self.config.padding_top + self.config.padding_bottom

    @property
    def config(self):
        return self._config

    @property
    def palette(self) -> Palette:
        return self._config.palette

    @property
    def background(self) -> QColor:
        return self.palette.get_color(Background)

    @property
    def cursor_color(self) -> QColor:
        return self.palette.get_color(Cursor)

    def cursor_rect(self, cursor_height=4) -> QRect:
        cursor = self.cursor
        x = self._x_pos(cursor.x)
        y = self._y_pos(cursor.y + 1) - cursor_height
        width = self._char_width(cursor.y, cursor.x)
        return QRect(x, y, width, cursor_height)

    def draw_dirty(self) -> None:
        self._draw_dirty()
        self.dirty.clear()

    @property
    def canvas(self) -> QPixmap:
        return self._canvas

    @property
    def paint_lock(self):
        return self._painter_lock

    def begin_draw(self):
        self._painter_lock.lock()

    def end_draw(self):
        self._painter_lock.unlock()

    # def resize(self, lines=None, columns=None):
    #     super(QtScreen, self).resize(lines, columns)
    #     self._canvas = self._create_canvas()
    #     self._draw_dirty(range(self.lines))
